#include <stdio.h>
#include <string.h>
#include <android/log.h>
#include <openssl/pkcs12.h>
#include <openssl/err.h>
#include "include/com_whyun_ndk_chapter4_jni_OpensslWrapper.h"
#define MAX_SIZE_FILE_PATH              256
#define MAX_SIZE_PASS_LEN               128

static void throwException(JNIEnv *env,const char *msg) {
	jclass exception = NULL;
	exception = (*env)->FindClass(env,"java/lang/Exception");
	if (exception != NULL)
	{
		(*env)->ThrowNew(env,exception,msg);
	}
}
/*
 * Class:     com_whyun_ndk_chapter4_jni_OpensslWrapper
 * Method:    parseP12File
 * Signature: (Ljava/lang/String;Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_com_whyun_ndk_chapter4_jni_OpensslWrapper_parseP12File
(JNIEnv *env, jobject obj, jstring filePath,jstring password) {
    PKCS12 *p12=NULL,*tmpP12=NULL;
	X509 *cert=NULL,*x5=NULL;//certificate defined
	STACK_OF(X509) *ca=NULL;//
	EVP_PKEY *pkey=NULL;//
    FILE *p12File = NULL;
    int ret;
    
    char *passwordStr = (char *)(*env)->GetStringUTFChars(env,password,NULL);
    int passwordLen = (*env)->GetStringUTFLength(env,password);
    
    char *filePathStr = (char *)(*env)->GetStringUTFChars(env,filePath,NULL);
    int filePathLen = (*env)->GetStringUTFLength(env,filePath);
    char filePathChars[MAX_SIZE_FILE_PATH] = {0};
    char passChars[MAX_SIZE_PASS_LEN] = {0};
    
    if (filePathLen >= MAX_SIZE_FILE_PATH) {
        throwException(env,"too long file path.");
        goto Cleanup;
    }
    strncpy(filePathChars,filePathStr,filePathLen);
    if ((p12File = fopen(filePathChars,"rb")) == NULL) {
        throwException(env,"can not open the p12 file.");
        goto Cleanup;
    }
    
    OpenSSL_add_all_algorithms();

    
	tmpP12 = d2i_PKCS12_fp(p12File,&p12);
	if (tmpP12 == NULL)
	{
		throwException(env,"invalid p12 file");
		goto Cleanup;
	}

	memset(passChars,0,MAX_SIZE_PASS_LEN);
	memcpy(passChars,passwordStr,passwordLen);
	ret=PKCS12_parse(p12,passChars,&pkey,&cert,&ca);
    if (ret !=1)
	{
        char msg[64] = {0};
        int err=ERR_peek_last_error();
        snprintf(msg,64,"parse p12 error:[%d].",ret);
        throwException(env,(const char*)msg);
        goto Cleanup;
    }
Cleanup:
    if (passwordStr != NULL) {
        (*env)->ReleaseStringUTFChars(env, password, passwordStr);
    }
    if (filePathStr != NULL) {
        (*env)->ReleaseStringUTFChars(env, filePath, filePathStr);
    }

    if(p12 != NULL)//has just modifyed
        PKCS12_free (p12);
    if (cert != NULL)//has just modifyed
        X509_free (cert);
    if (pkey != NULL)//has just modifyed
        EVP_PKEY_free (pkey);
    if (ca != NULL)
    {
        for(;;)
        {
            x5 = sk_X509_pop(ca);
            if (!x5)
                break;
            X509_free(x5);
        }
        sk_X509_free (ca);
    }
}
